﻿using System;
using System.Diagnostics;
using System.Linq;
using System.Drawing;
using System.IO;
using FreeImageAPI;
using RayDen.Library.Core.Collections;
using RayDen.Library.Core.Primitives;

namespace RayDen.Library.Data.Imaging
{
    public static class ImageFactory
    {

        public static ulong MemoryOccupied;

        public static RgbSpectrum[] GetDataFromBitmap(Bitmap bmp)
        {
            var result = new RgbSpectrum[bmp.Width * bmp.Height];
            for (int i = 0; i < bmp.Height; i++)
            {
                for (int j = 0; j < bmp.Width; j++)
                {
                    result[j + i * bmp.Width] = RgbSpectrum.Create(bmp.GetPixel(j, i));
                }
            }
            return result;
        }

        public static void ApplyWaterMark(int x, int y, Bitmap bmp, string[] watermark)
        {
            if (watermark.Length == 0)
                return;
            int yOffset = (int)(SystemFonts.DefaultFont.SizeInPoints * 1.5f);
            int i = 0;
            var pix = bmp.GetPixel(x, y);

            var textColor = Color.FromArgb(255 - pix.R, 255 - pix.G, 255 - pix.B);

            using (var g = Graphics.FromImage(bmp))
            {
                using (var br = new SolidBrush(textColor))
                    foreach (var s in watermark)
                    {
                        g.DrawString(s, SystemFonts.DefaultFont, br, x, y + i * yOffset);
                        i++;
                    }
            }
        }

        public static RgbSpectrumTexture FreeImageLoadExrBitmap(string fileName)
        {

            Console.WriteLine("..Loading hdr bitmap {0}", fileName);
            using (var bmp = new FreeImageBitmap(fileName))
            {
                //bmp.AdjustGamma(2.2);
                var pdata = new RgbSpectrumInfo[bmp.Width * bmp.Height];
                for (int i = 0; i < bmp.Height; i++)
                {
                    Scanline<FIRGBF> s = bmp.GetScanline<FIRGBF>(i);
                    for (int j = 0; j < bmp.Width; j++)
                    {
                        pdata[j + i * bmp.Width] = new RgbSpectrumInfo(s[j].red, s[j].green, s[j].blue);
                        //pdata[j + i * bmp.Width] .DeGamma();
                    }
                }
                Console.WriteLine("... Converted size {0:F5} MBytes", (bmp.Width * bmp.Height * 12.0f) / (1024f * 1024f));
                MemoryOccupied += (ulong)(bmp.Width * bmp.Height * 12u);
                var tex = new RgbSpectrumTexture(fileName, pdata, bmp.Width, bmp.Height);
                return tex;
            }
        }

        public static RgbSpectrumTexture FreeImageLoadBitmap(string fileName)
        {
            Console.WriteLine("..Loading ldr bitmap {0}", fileName);
            using (var bmp = new FreeImageBitmap(fileName))
            {
                //bmp.AdjustGamma(2.2);

                var pdata = new RgbSpectrumInfo[bmp.Width * bmp.Height];
                for (int i = 0; i < bmp.Height; i++)
                {

                    if (bmp.ColorDepth == 24)
                    {
                        Scanline<RGBTRIPLE> s = bmp.GetScanline<RGBTRIPLE>(i);
                        for (int j = 0; j < bmp.Width; j++)
                        {
                            pdata[j + i * bmp.Width] =
                                RgbSpectrumInfo.Create(s[j].rgbtRed, s[j].rgbtGreen, s[j].rgbtBlue, false).Abs();
                        }
                    }

                    if (bmp.ColorDepth == 32)
                    {
                        Scanline<RGBQUAD> s = bmp.GetScanline<RGBQUAD>(i);
                        for (int j = 0; j < bmp.Width; j++)
                        {
                            pdata[j + i * bmp.Width] =
                                RgbSpectrumInfo.Create(s[j].rgbRed, s[j].rgbGreen, s[j].rgbBlue, false).Abs();
                        }
                    }

                    if (bmp.ColorDepth == 8)
                    {

                        Scanline<byte> s = bmp.GetScanline<byte>(i);
                        for (int j = 0; j < bmp.Width; j++)
                        {
                            pdata[j + i * bmp.Width] =
                                RgbSpectrumInfo.Create(bmp.Palette[s[j]].rgbRed, bmp.Palette[s[j]].rgbGreen, bmp.Palette[s[j]].rgbBlue, false).Abs();
                        }
                    }

                }
                Console.WriteLine("... Converted size {0:F5} MBytes", (bmp.Width * bmp.Height * 12.0f) / (1024f * 1024f));
                MemoryOccupied += (ulong)(bmp.Width * bmp.Height * 12u);

                var tex = new RgbSpectrumTexture(fileName, pdata, bmp.Width, bmp.Height);
                return tex;
            }
        }

        public static void SaveSRgbTexture(string fileName, int width, int height, RgbSpectrum[] data)
        {
            using (var fs = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write))
            {
                using (var writer = new BinaryWriter(fs))
                {
                    writer.Write(width);
                    writer.Write(height);
                    var byteData = new byte[data.Length * 12];
                    Buffer.BlockCopy(data, 0, byteData, 0, data.Length * 12);
                    writer.Write(byteData);
                }
            }
        }

        public static RgbSpectrumTexture SaveSRgbTexture(string fileName)
        {
            int width;
            int height;
            RgbSpectrum[] data;
            using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
            {
                using (var reader = new BinaryReader(fs))
                {
                    width = reader.ReadInt32();
                    height = reader.ReadInt32();
                    var byteData = new byte[width * height * 12];
                    reader.Read(byteData, 0, width * height * 12);
                    var floatData = new float[width * height * 4];
                    Buffer.BlockCopy(byteData, 0, floatData, 0, width * height * 12);
                    data = floatData.ToSpectrumList().ToArray();
                    return new RgbSpectrumTexture(fileName, data, width, height);
                }
            }
        }

        public static void FreeImageSaveExrBitmap(string fileName, int width, int height, RgbSpectrum[] data, string[] watermark = null)
        {
            using (var bmp = new FreeImageBitmap(width, height, FREE_IMAGE_TYPE.FIT_RGBF))
            {
                for (int i = 0; i < bmp.Height; i++)
                {
                    Scanline<FIRGBF> s = bmp.GetScanline<FIRGBF>(i);
                    for (int j = 0; j < bmp.Width; j++)
                    {
                        try
                        {
                            var spectra = data[j + i * bmp.Width];
                            s[j] = new FIRGBF()
                            {
                                blue = spectra.c3,
                                green = spectra.c2,
                                red = spectra.c1
                            };
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message);
                        }
                    }
                }
                if (watermark != null)
                {
                    ApplyWaterMark(10, 400, bmp.ToBitmap(), watermark);
                }

                bmp.Save(fileName, FREE_IMAGE_FORMAT.FIF_EXR, FREE_IMAGE_SAVE_FLAGS.EXR_NONE);
                bmp.TmoReinhard05(1, 0.4);
                bmp.Save(fileName, FREE_IMAGE_FORMAT.FIF_PNG);
            }
        }


    }
}
